/*
@file	Circle.cpp
@author	Saume
*/

#include "Circle.h"
#include "CustomRectangle.h"
#include "LineSegment.h"

Circle::Circle(): 
Polygon(), 
radius(0.0f) {
	
}

Circle::Circle(float xVal, float yVal, float radVal): 
Polygon(xVal, yVal) {
	radius = (radVal < 0) ? -radVal : radVal;
}

Circle::Circle(const Vector* const v, const float radVal): 
Polygon(v) {
	radius = (radVal < 0) ? -radVal : radVal;
}

/*
Determines wether or not the given x and y values is inside the radius of the circle.
*/
bool Circle::Contains(float xVal, float yVal) const {
	if (SqDistanceWith(xVal, yVal) <= GetRadiusSq()) {
		return true;
	}
	return false;
}

/*
Determines wether or not the given point vector is inside the radius of the circle.
*/
bool Circle::Contains(const Vector* const v) const {
	return Contains(v->x, v->y);
}

/*
Returns the radius of the circle.
*/
float Circle::GetRadius() const {
	return radius;
}

/*
Returns the square of the radius of the circle.
*/
float Circle::GetRadiusSq() const {
	return radius * radius;
}

/*
Determines wether or not the circle collides with the given circle.
*/
bool Circle::CollidesWith(const Circle* const c) const {
	float radSumSq = GetRadius() + c->GetRadius();
	radSumSq *= radSumSq; // square it
	if (SqDistanceWith(c->x, c->y) <= radSumSq) {
		return true;
	}
	return false;
}

/*
Determines wether or not the circle collides with the given rectangle.
*/
bool Circle::CollidesWith(const CustomRectangle* const r) const {
	if (r->Contains(x, y)) { // if the center of the circle is inside the rectangle
		return true; // in case of a small circle inside a big rectangle
	}
	if (Contains(r->GetX(), r->GetY())) { // if the rectangle is inside the circle
		return true; // in case of a small rectangle inside a big circle
	}

	// create a line segment for each of the sides of the rectangle
	LineSegment* top = new LineSegment(r->GetX(), r->GetY(), r->GetX() + r->GetWidth(), r->GetY());
	LineSegment* right = new LineSegment(top->GetTo()->x, top->GetTo()->y, r->GetX() + r->GetWidth(), r->GetY() + r->GetHeight());
	LineSegment* bottom = new LineSegment(r->GetX(), r->GetY() + r->GetHeight(), right->GetTo()->x, right->GetTo()->y);
	LineSegment* left = new LineSegment(top->GetFrom(), bottom->GetFrom());

	// if any of the sides intersect with the circle, it means there is a collision
	if (top->IntersectsWith(this) || right->IntersectsWith(this) || bottom->IntersectsWith(this) || left->IntersectsWith(this)) {
		return true;
	}

	return false;
}

/*
Returns the square of the distance with given x and y values.
*/
float Circle::SqDistanceWith(float xVal, float yVal) const {
	float dx = xVal - x;
	float dy = yVal - y;
	return dx * dx + dy * dy;
}

/*
Returns the square of the distance with given point vector.
*/
float Circle::SqDistanceWith(const Vector* const v) const {
	return SqDistanceWith(v->x, v->y);
}

/*
Displays the circle in the console.
*/
void Circle::Display() const {
	std::cout << "(" << x << ", " << y << "), " << radius << "r";
}

/*
Determines wether two circles are at the same position and size or not.
*/
bool operator==(const Circle& c1, const Circle& c2) {
	float dx = c2.GetX() - c1.GetX();
	float dy = c2.GetY() - c1.GetY();
	float dr = c2.GetRadius() - c1.GetRadius();
	dx = fabs(dx);
	dy = fabs(dy);
	dr = fabs(dr);

	return dx <= EPSILON_DBL && dy <= EPSILON_DBL && dr <= EPSILON_DBL;
}